<?php
/**
 * Provides ICMP functionality for monitoring purposes
 */
class BRail_Network_Icmp
{
    /**
     * Calculate ICMP checksum
     * @param string $data
     * @return string
     */
    protected static function icmpChecksum($data)
    {
        if (strlen($data) % 2) {
            $data .= "\x00";
        }

        $bit = unpack('n*', $data);
        $sum = array_sum($bit);

        while ($sum >> 16) {
            $sum = ($sum >> 16) + ($sum & 0xffff);
        }

        return pack('n*', ~$sum);
    }

    /**
     * Sends an ICMP echo packet (PING) to a host
     * See http://www.planet-source-code.com/vb/scripts/ShowCode.asp?lngWId=8&txtCodeId=1786
     * @param string $host The hostname to check
     * @return bool True if host is up, false if host is down
     */
    public static function ping($host)
    {
        if (true === extension_loaded('sockets')) {
            $package = "\x08\x00\x00\x00\x00\x00\x00\x00Scarface";
            $checksum = self::icmpChecksum($package);
            $package = "\x08\x00" . $checksum . "\x00\x00\x00\x00Scarface";

            $socket = socket_create(AF_INET, SOCK_RAW, 1);
            socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 2, 'usec' => 0));
            $oldErrorLevel = error_reporting('E_NONE');
            $connection = socket_connect($socket, $host, null);
            error_reporting($oldErrorLevel);
            if ($connection === false) {
                return false;
            }

            $startTime = microtime(true);
            socket_send($socket, $package, strlen($package), 0);
            if (false === (@socket_read($socket, 255))) {
                return false;
            } else {
                return true;
            }
            socket_close($socket);
        } else {
            throw new Zend_Exception('PHP sockets extension not loaded - required for network ping test');
        }
    }
}
